#include "headers.h"

Parser::Parser(InfoWnd* infoWnd)
{
    for(int i=0; i<KEYWORDS.length(); i++)
        SymbolTable.insert(QPair<QString, int>(KEYWORDS.at(i), 0), Description("KEYWORD"));
    this->infoWnd=infoWnd;
    defPTfile="default.xml";
    if(!LoadParserTable(defPTfile))
    {
        if(QMessageBox::information(NULL, QObject::tr("Load parser"), QObject::tr("Do you want to specify other file?"), QMessageBox::Cancel|QMessageBox::Ok)==QMessageBox::Ok)
        {
            QString newFileName=QFileDialog::getOpenFileName(NULL, QObject::tr("Open file"), NULL, "XML file (*.xml)");
            LoadParserTable(newFileName);
        }
    }
}

bool Parser::ParseFile(QString fileName)
{
    QFile* file=new QFile(fileName);
    if(!file->exists())
    {
        //error=new Error(0xfff0, "File doesn't exists");
        return false;
    }
    if(!file->open(QIODevice::ReadOnly | QIODevice::Text))
    {
        //error=new Error(0xfff1, "File can't be open for reading "+file.errorString());
        return false;
    }
    infoWnd->errorsList->clear();
    QList<Lexeme>* lexemes=new QList<Lexeme>();
    Error* error=new Error();
    if(!lex.Analyze(file, lexemes, &SymbolTable, error))
    {
        PrintLexemes(lexemes);
        infoWnd->errorsList->addItem(QObject::tr("Lexical Errror #%1 at line %2 on position %3 fount.\n    Error text: \"%4\"").arg(error->code).arg((error->userValue!=NULL)?((QPoint*)error->userValue)->x():(-1)).arg((error->userValue!=NULL)?((QPoint*)error->userValue)->y():(-1)).arg(error->text));
        return false;
    }
    PrintLexemes(lexemes);

    {
        infoWnd->symbolTable->clear();
        QList<QPair<QString, int> > list=SymbolTable.keys();
        for(int i=0; i<list.size(); i++)
        {
            infoWnd->symbolTable->addItem(list.at(i).first+" "+QString::number(list.at(i).second));
        }
//        for(QList<QPair<QString, int> >::iterator iter=SymbolTable.keys().begin(); iter!=SymbolTable.keys().end(); ++iter)
//        {
//            infoWnd->symbolTable->addItem((*iter).first+" "+(*iter).second);
//        }
    }

    if(!synt.Analyze(lexemes, infoWnd->derivationTree, error))
    {
        infoWnd->errorsList->addItem(QObject::tr("Sintactical Errror #%1 at line %2 on position %3 fount.\n    Error text: \"%4\"").arg(error->code).arg((error->userValue!=NULL)?((QPoint*)error->userValue)->x():(-1)).arg((error->userValue!=NULL)?((QPoint*)error->userValue)->y():(-1)).arg(error->text));
        return false;
    }

    return true;
}

bool Parser::LoadParserTable(QString fileName)
{
    QString errorMsg;
    int errorLine;
    int errorColumn;

    QFile file(fileName);

    if(!file.exists())
    {
        QMessageBox::information(NULL, QObject::tr("JCmp"),
                                 QObject::tr("File \'%1\' doesnt exists")
                                 .arg(fileName));
        return false;
    }
    if(!file.open(QIODevice::ReadOnly|QIODevice::Text))
    {
        QMessageBox::information(NULL, QObject::tr("JCmp"),
                                 QObject::tr("Error while opening file: %1")
                                 .arg(file.errorString()));
        return false;
    }
    QDomDocument domDoc;
    if(!domDoc.setContent(&file, true, &errorMsg, &errorLine, &errorColumn))
    {
        QMessageBox::information(NULL, QObject::tr("JCmp"),
                                 QObject::tr("Parse error at line %1, column %2:\n%3")
                                 .arg(errorLine)
                                 .arg(errorColumn)
                                 .arg(errorMsg));
        return false;
    }
    QDomElement root=domDoc.documentElement();
    if(root.tagName()!="parser_tables")
    {
        QMessageBox::information(NULL, QObject::tr("JCmp"),
                                 QObject::tr("The file doesn't contains parser tables description"));
        return false;
    }
    QDomElement child=root.firstChildElement("grammar");
    if(!grammar.ReadFromXML(child))
        return false;
    synt.SetRules(&grammar.rules);

    child=root.firstChildElement("tables");
    if(child.isNull())
    {
        QMessageBox::information(NULL, QObject::tr("JCmp"),
                                 QObject::tr("The file doesn't contains control tables description"));
        return false;
    }
    QDomElement tableChild=child.firstChildElement("states");
    if(tableChild.isNull())
    {
        QMessageBox::information(NULL, QObject::tr("JCmp"),
                                 QObject::tr("The file doesn't contains states description"));
        return false;
    }
    QList<int> states;
    foreach(QString str, tableChild.text().split(QRegExp("\\s+"), QString::SkipEmptyParts))
    {
        bool ok;
        states.append(str.toInt(&ok));
        if(!ok)
        {
            QMessageBox::information(NULL, QObject::tr("JCmp"),
                                     QObject::tr("Wrong value in states list"));
            return false;
        }
    }
    if(states.isEmpty())
    {
        QMessageBox::information(NULL, QObject::tr("JCmp"),
                                 QObject::tr("Empty list of states"));
        return false;
    }
    QList<QString> tmp=grammar.terminals.toList();
    tmp.append("");
    synt.actionTable=new ControlTable(states, tmp);
    synt.gotoTable=new ControlTable(states, grammar.nonterminals.toList());

    tableChild=child.firstChildElement("action_table");
    QDomNodeList nodelist = tableChild.childNodes();
    for(int i=0; i<nodelist.count(); ++i)
    {
        QDomElement cell=nodelist.at(i).toElement();
        if(synt.actionTable->SetValue(cell.attribute("state").toInt(), cell.attribute("symbol"), Cell(cell.attribute("action").toInt(), cell.attribute("value").toInt())))
        {
            QMessageBox::information(NULL, QObject::tr("JCmp"),
                                     QObject::tr("Wrong cell value in action_table with attribues action=\'%1\', symbol=\'%2\',  value=\'%3\', state=\'%4\'")
                                     .arg(cell.attribute("action"))
                                     .arg(cell.attribute("symbol"))
                                     .arg(cell.attribute("value"))
                                     .arg(cell.attribute("state")));
            return false;
        }
    }

    tableChild=child.firstChildElement("goto_table");
    nodelist = tableChild.childNodes();
    for(int i=0; i<nodelist.count(); ++i)
    {
        QDomElement cell=nodelist.at(i).toElement();
        if(synt.gotoTable->SetValue(cell.attribute("state").toInt(), cell.attribute("symbol"), Cell(cell.attribute("action").toInt(), cell.attribute("value").toInt())))
        {
            QMessageBox::information(NULL, QObject::tr("JCmp"),
                                     QObject::tr("Wrong cell value in goto_table with attribues action=\'%1\', symbol=\'%2\',  value=\'%3\', state=\'%4\'")
                                     .arg(cell.attribute("action"))
                                     .arg(cell.attribute("symbol"))
                                     .arg(cell.attribute("value"))
                                     .arg(cell.attribute("state")));
            return false;
        }
    }

    PrintParserTables();
    return true;
}

void Parser::PrintParserTables()
{
    infoWnd->nonterminalsList->clear();
    infoWnd->nonterminalsList->addItems(grammar.getNonterminals());
    infoWnd->terminalsList->clear();
    infoWnd->terminalsList->addItems(grammar.getTerminals());
    infoWnd->rulesList->clear();
    infoWnd->rulesList->addItems(grammar.getRules());
    infoWnd->firstSymbolTXT->setText(grammar.getFirstSymbol());

    infoWnd->actionTableWidget->setRowCount(synt.actionTable->GetHeight());
    infoWnd->actionTableWidget->setColumnCount(synt.actionTable->GetWidth());
    infoWnd->actionTableWidget->setHorizontalHeaderLabels(synt.actionTable->keys2());
    infoWnd->actionTableWidget->setVerticalHeaderLabels(synt.actionTable->keys1string());

    infoWnd->gotoTableWidget->setRowCount(synt.gotoTable->GetHeight());
    infoWnd->gotoTableWidget->setColumnCount(synt.gotoTable->GetWidth());
    infoWnd->gotoTableWidget->setHorizontalHeaderLabels(synt.gotoTable->keys2());
    infoWnd->gotoTableWidget->setVerticalHeaderLabels(synt.gotoTable->keys1string());

    foreach(int key1, synt.actionTable->keys1())
        foreach(QString key2, synt.actionTable->keys2())
        {
            Cell* cell=new Cell();
            synt.actionTable->GetValue(key1, key2, cell);
            infoWnd->actionTableWidget->setItem(synt.actionTable->keys1().indexOf(key1), synt.actionTable->keys2().indexOf(key2), new QTableWidgetItem(cell->ToString()));
        }
    foreach(int key1, synt.gotoTable->keys1())
        foreach(QString key2, synt.gotoTable->keys2())
        {
            Cell* cell=new Cell();
            synt.gotoTable->GetValue(key1, key2, cell);
            infoWnd->gotoTableWidget->setItem(synt.gotoTable->keys1().indexOf(key1), synt.gotoTable->keys2().indexOf(key2), new QTableWidgetItem(cell->ToString()));
        }
}

void Parser::PrintLexemes(QList<Lexeme>* lexemes)
{
    infoWnd->lexemesList->clear();
    infoWnd->lexemesList->addItem("Number\tLexeme");
    infoWnd->lexemesList->addItem("---------------------------------------------------------------------------------------------------------------------------------");
    int count=1;
    foreach(Lexeme lex, *lexemes)
    {
        infoWnd->lexemesList->addItem(QObject::tr("#%1\t%2").arg(count++).arg(lex.ToString()));
    }
}
